package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.model.Option;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.system.convert.DictTypeConverter;
import com.dimples.dd.system.mapper.SystemDictTypeMapper;
import com.dimples.dd.system.model.entity.SystemDictDataDO;
import com.dimples.dd.system.model.entity.SystemDictTypeDO;
import com.dimples.dd.system.model.form.DictTypeForm;
import com.dimples.dd.system.model.query.DictTypePageQuery;
import com.dimples.dd.system.model.vo.DictTypePageVO;
import com.dimples.dd.system.service.SystemDictDataService;
import com.dimples.dd.system.service.SystemDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.*;

@Service
@RequiredArgsConstructor
public class SystemDictTypeServiceImpl extends ServiceImpl<SystemDictTypeMapper, SystemDictTypeDO> implements SystemDictTypeService {

    private final DictTypeConverter dictTypeConverter;
    private final SystemDictDataService systemDictDataService;

    @Override
    public PageResult<DictTypePageVO> selectDictTypePage(DictTypePageQuery queryParams) {
        PageResult<SystemDictTypeDO> typePageResult = this.baseMapper.selectPage(queryParams);
        return this.dictTypeConverter.entity2Page(typePageResult);
    }

    @Override
    public DictTypeForm selectDictTypeForm(Long id) {
        SystemDictTypeDO dictTypeDO = this.baseMapper.selectById(id);
        return this.dictTypeConverter.entity2Form(dictTypeDO);
    }

    @Override
    public boolean createDictType(DictTypeForm dictTypeForm) {
        // 校验字典类型的名字的唯一性
        validateDictTypeNameUnique(null, dictTypeForm.getName());
        // 校验字典类型的类型的唯一性
        validateDictTypeUnique(null, dictTypeForm.getType());

        // 插入字典类型
        SystemDictTypeDO dictType = this.dictTypeConverter.form2Entity(dictTypeForm);
        dictType.setDeletedTime(LocalDateTimeUtil.of(0)); // 唯一索引，避免 null 值
        this.baseMapper.insert(dictType);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateDictType(Long id, DictTypeForm dictTypeForm) {
        // 校验自己存在
        SystemDictTypeDO dictTypeDO = validateDictTypeExists(id);
        // 校验字典类型的名字的唯一性
        validateDictTypeNameUnique(id, dictTypeForm.getName());
        // 校验字典类型的类型的唯一性
        validateDictTypeUnique(id, dictTypeForm.getType());

        dictTypeForm.setId(id);

        // 更新字典类型
        SystemDictTypeDO updateObj = this.dictTypeConverter.form2Entity(dictTypeForm);
        int update = this.baseMapper.updateById(updateObj);
        if (update > 0) {
            // 字典类型code变化，同步修改字典项的类型code
            String oldCode = dictTypeDO.getType();
            String newCode = dictTypeForm.getType();
            if (!StrUtil.equals(oldCode, newCode)) {
                this.systemDictDataService.update(new LambdaUpdateWrapper<SystemDictDataDO>()
                        .eq(SystemDictDataDO::getDictType, oldCode)
                        .set(SystemDictDataDO::getDictType, newCode)
                );
            }
        }
        return update > 0;
    }

    @VisibleForTesting
    void validateDictTypeNameUnique(Long id, String name) {
        SystemDictTypeDO dictType = this.baseMapper.selectByName(name);
        if (dictType == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的字典类型
        if (id == null) {
            throw exception(DICT_TYPE_NAME_DUPLICATE);
        }
        if (!dictType.getId().equals(id)) {
            throw exception(DICT_TYPE_NAME_DUPLICATE);
        }
    }

    @VisibleForTesting
    void validateDictTypeUnique(Long id, String type) {
        if (StrUtil.isEmpty(type)) {
            return;
        }
        SystemDictTypeDO dictType = this.baseMapper.selectByType(type);
        if (dictType == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的字典类型
        if (id == null) {
            throw exception(DICT_TYPE_TYPE_DUPLICATE);
        }
        if (!dictType.getId().equals(id)) {
            throw exception(DICT_TYPE_TYPE_DUPLICATE);
        }
    }

    @VisibleForTesting
    SystemDictTypeDO validateDictTypeExists(Long id) {
        if (id == null) {
            return null;
        }
        SystemDictTypeDO dictType = this.baseMapper.selectById(id);
        if (dictType == null) {
            throw exception(DICT_TYPE_NOT_EXISTS, id);
        }
        return dictType;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteDictTypes(String idsStr) {
        List<Long> ids = Arrays.stream(StrUtil.splitToLong(idsStr, StrUtil.COMMA)).boxed().collect(Collectors.toList());

        // 校验是否存在
        List<SystemDictTypeDO> dictTypeList = validateDictTypeExists(ids);
        // 校验是否有字典数据
        assert dictTypeList != null;
        List<String> dictTypeIds = this.systemDictDataService.selectDictTypeByTypeCode(dictTypeList.stream().map(SystemDictTypeDO::getType).collect(Collectors.toList()));
        if (CollUtil.isNotEmpty(dictTypeIds)) {
            throw exception(DICT_TYPE_HAS_CHILDREN, StrUtil.join(StrUtil.COMMA, dictTypeIds));
        }

        // 删除字典数据项
        List<String> dictTypeCodes = this.list(new LambdaQueryWrapper<SystemDictTypeDO>().in(SystemDictTypeDO::getId, ids).select(SystemDictTypeDO::getType))
                .stream().map(SystemDictTypeDO::getType).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(dictTypeCodes)) {
            this.systemDictDataService.remove(new LambdaQueryWrapper<SystemDictDataDO>().in(SystemDictDataDO::getDictType, dictTypeCodes));
        }
        // 删除字典类型
        return this.removeByIds(ids);
    }

    private List<SystemDictTypeDO> validateDictTypeExists(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return null;
        }
        List<SystemDictTypeDO> batchIds = this.baseMapper.selectBatchIds(ids);
        if (CollUtil.isEmpty(batchIds)) {
            throw exception(DICT_TYPE_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, ids));
        } else {
            List<Long> noExistId = CollUtil.subtractToList(ids, batchIds.stream().map(SystemDictTypeDO::getId).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(noExistId)) {
                throw exception(DICT_TYPE_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, noExistId));
            }
        }
        return batchIds;
    }

    @Override
    public List<Option<Long>> listDictItemsByTypeCode(String typeCode) {
        // 数据字典项
        List<SystemDictTypeDO> dictTypes = this.baseMapper.selectList(new LambdaQueryWrapper<SystemDictTypeDO>().eq(SystemDictTypeDO::getType, typeCode).select(SystemDictTypeDO::getId, SystemDictTypeDO::getName)
        );

        // 转换下拉数据
        return CollectionUtil.emptyIfNull(dictTypes).stream().map(dictItem -> new Option<Long>(dictItem.getId(), dictItem.getName())).collect(Collectors.toList());
    }

    @Override
    public SystemDictTypeDO selectDictType(String type) {
        return this.baseMapper.selectByType(type);
    }

    @Override
    public List<SystemDictTypeDO> selectListByStatus(Integer status) {
        return this.baseMapper.selectList(SystemDictTypeDO::getStatus, status);
    }
}















